/* ==============================================================================
*
*   Copyright (c) 2010, Pierre Favier
*   All rights reserved.
*
*   NTasking is free software: you can redistribute it and/or modify it
*   under the terms of the GNU General Public License as published by the
*   Free Software Foundation.
*
*   Redistribution and use in source and binary forms, with or without
*   modification, are permitted provided that the following conditions
*   are met:
*
*   -   Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*
*   -   Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials
*       provided with the distribution.
*
*   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
*   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
*   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
*   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
*   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
*   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
*   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
*   THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*   SUCH DAMAGE.
*
* ============================================================================== */

using System;

namespace NTasking.Internals
{

    /// <summary>
    /// The <see cref="MethodCall"/>  define the Caller-side aspect of a synchronized method call
    /// </summary>
    public abstract class MethodCall : IContinuable
    {

        #region Fields

        private bool _cancelable = true;


        private MethodCall _requeued;


        /// <summary>
        /// A value representing the task from which the call has been performed.
        /// </summary>
        internal readonly ThreadWrapper Thread;


        /// <summary>
        /// Gets or sets the synchronised method of which this instance was a call before any requeue occured.
        /// </summary>
        /// <value>
        /// The synchronised method of which this instance was a call before any requeue occured.
        /// </value>
        internal readonly SynchronizedMethod OriginalTarget;


        /// <summary>
        /// The context of the call.
        /// </summary>
        internal readonly CallContext Context;

        #endregion


        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="MethodCall"/> is finished.
        /// </summary>
        /// <value><c>true</c> if finished; otherwise, <c>false</c>.</value>
        internal bool Finished { get; set; }


        /// <summary>
        /// The <see cref="ManagedLock"/> used to prevent against concurrent access
        /// to this instance.
        /// </summary>
        internal ManagedLock CallLock { get { return Thread.CallLock; } }


        /// <summary>
        /// Gets or sets the state in which the current thread is waiting.
        /// </summary>
        /// <value>The state of the wait.</value>
        internal CallerWaitState WaitState { get; set; }


        /// <summary>
        /// Gets or sets a delegate representing the sequence of statements to be executed after this
        /// instance has been accepted.
        /// </summary>
        /// <value>A delegate representing the sequence of statements to be executed.</value>
        public Action Continuation { get; set; }


        /// <summary>
        /// Gets or sets a value indicating whether this instance has an abortable part.
        /// </summary>
        /// <value>
        ///     <c>true</c> if this instance has an abortable part; otherwise, <c>false</c>.
        /// </value>
        internal bool HasAbortablePart { get; set; }


        /// <summary>
        /// Gets a value indicating whether this instance has been requeued to a different entry.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance has been requeued to a different entry; <c>false</c> otherwise.
        /// </value>
        internal bool IsRequeued
        {
            get { return _requeued != null; }
        }


        /// <summary>
        /// Gets or sets a <see cref="MethodCall"/> to which this instance has been requeued.
        /// </summary>
        /// <value>
        /// The call to which this instance has been requeued or this instance if no requeue ocurred.
        /// </value>
        internal MethodCall Requeued
        {
            get { return _requeued ?? this; }

            set { _requeued = value; }
        }


        /// <summary>
        /// Gets or sets the synchronised method of which this instance represents a call after
        /// any potential requeue occured. This property returns a single entry or an entry family.
        /// </summary>
        /// <value>
        /// The synchronised method of which this instance represents a call after any potential
        /// requeue occured.
        /// </value>
        public SynchronizedMethod Target
        {
            get { return Requeued.OriginalTarget.Family; }
        }


        /// <summary>
        /// Gets or sets the synchronised method instance of which this instance represents a call after
        /// any potential requeue occured. This property returns a single entry or an entry family member.
        /// </summary>
        /// <value>
        /// The synchronised method instance of which this instance represents a call after any potential
        /// requeue occured.
        /// </value>
        internal SynchronizedMethod TargetInstance
        {
            get { return Requeued.OriginalTarget; }
        }


        /// <summary>
        /// Gets or Sets a value indicating whether the set of call is finished
        /// </summary>
        /// <value><c>true</c> if the call set is finished; otherwise <c>false</c>.</value>
        internal virtual bool Cancelable
        {
            get
            {
                return _cancelable;
            }
            set
            {
                if (_cancelable == value)
                    return;

                _cancelable = value;

                if (_cancelable)
                    Thread.AbortResume();
                else
                    Thread.AbortDefer();
            }
        }

        #endregion


        #region Constructors

        /// <summary>
        /// Initialize a <see cref="MethodCall"/>
        /// </summary>
        internal MethodCall(SynchronizedMethod originalTarget, CallContext context)
        {
            Thread = ThreadWrapper.CurrentThread;
            OriginalTarget = originalTarget;
            Context = context;
        }

        #endregion


        #region Methods

        /// <summary>
        /// Execute the sequence of statements to be executed after the call has been accepted.
        /// </summary>
        public virtual void Continue()
        {
            Context.CheckResult();

            if (Continuation != null)
                Continuation();
        }


        /// <summary>
        /// Check if the specified <see cref="MethodCall"/> matches this instance with regard to family index.
        /// </summary>
        /// <param name="context">The <see cref="MethodCall"/> to be checked.</param>
        /// <returns><c>true</c> if the specified <see cref="MethodCall"/> matches; <c>false</c> otherwise.</returns>
        internal virtual bool MatchFamilyMember(MethodCall context)
        {
            return true;
        }


        internal void CancelSelectiveCallAlternative()
        {
            lock (Thread.WaitStateLock)
            {
                switch (WaitState)
                {
                    case CallerWaitState.ExecuteAbortablePart:
                        Thread.Interrupt(this);
                        break;
                    case CallerWaitState.WaitForTimeout:
                        CallLock.Pulse();
                        break;
                }
            }
        }


        /// <summary>
        /// Returns a string representing this instance's WaitState.
        /// </summary>
        /// <returns>A string representing this instance's WaitState</returns>
        internal string StateString()
        {
            switch (WaitState)
            {
                case CallerWaitState.ExecuteAbortablePart:
                    return "Executing abortable part of " + this;

                case CallerWaitState.WaitForTimeout:
                    return "Waiting for timeout of " + this;

                case CallerWaitState.WaitForCompletion:
                    return "Waiting for completion of " + this;

                default:
                    return "Running";
            }
        }


        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return IsRequeued ?
                string.Format("{0}{1} requeued to {2}", OriginalTarget.FullName(), Context, Requeued.ToString(Context)) :
                string.Format("{0}{1}", TargetInstance.FullName(), Context);
        }


        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current call with the given parameters
        /// </summary>
        /// <param name="context">The parameters with which this instance was called.</param>
        /// <returns>A <see cref="T:System.String"/> that represents the current call</returns>
        internal string ToString(CallContext context)
        {
            return string.Format("{0}{1}", TargetInstance.FullName(), context.ToString(TargetInstance.ParametersCount));
        }

        #endregion

    }


    /// <summary>
    /// A specialization of <see cref="MethodCall"/> for synchronized methods with a given return type
    /// </summary>
    /// <typeparam name="RetT">The type returned by the synchronized method.</typeparam>
    public class MethodCall<RetT> : MethodCall, IContinuable<RetT>
    {

        #region Fields

        /// <summary>
        /// Gets or sets the context of the call.
        /// </summary>
        /// <value>The context of the call.</value>
        internal new readonly CallContext<RetT> Context;

        #endregion


        #region Properties

        /// <summary>
        /// Gets or sets a delegate representing a sequence of statements to be executed after the call has been accepted.
        /// This sequence of statements will be passed the result of the call as a parameter.
        /// </summary>
        /// <value>The sequence of statements to be executed after the call has been accepted.</value>
        public Action<RetT> ContextualContinuation { get; set; }

        #endregion


        #region Constructors

        internal MethodCall(SynchronizedMethod originalTarget, CallContext<RetT> context)
            : base(originalTarget, context)
        {
            Context = context;
        }

        #endregion


        #region Methods

        /// <summary>
        /// Execute the sequence of statements to be executed after the call has been accepted.
        /// </summary>
        public override void Continue()
        {
            Context.CheckResult();

            if (Continuation != null)
                Continuation();
            else if (ContextualContinuation != null)
                ContextualContinuation(Context.Result);
        }

        #endregion

    }


    /// <summary>
    /// A specialization of <see cref="MethodCall"/> for method families
    /// </summary>
    /// <typeparam name="RetT">The type returned by the synchronized method family.</typeparam>
    /// <typeparam name="IndexT">The type of the method family's index.</typeparam>
    public class MethodCall<IndexT, RetT> : MethodCall<RetT>, IIndexed<IndexT>
    {

        /// <summary>
        /// Gets or sets the index of the method family's member which is called.
        /// </summary>
        /// <value>The index of the method family's member which is called.</value>
        public IndexT Index { get; set; }


        internal MethodCall(SynchronizedMethod originalTarget, CallContext<RetT> context, IndexT index)
            : base(originalTarget, context)
        {
            Index = index;
        }


        /// <summary>
        /// Check if the specified <see cref="MethodCall"/> matches this instance with regard to family index.
        /// </summary>
        /// <param name="context">The <see cref="MethodCall"/> to be checked.</param>
        /// <returns>
        ///     <c>true</c> if the specified <see cref="MethodCall"/> matches; <c>false</c> otherwise.
        /// </returns>
        internal override bool MatchFamilyMember(MethodCall context)
        {
            if (IsRequeued)
                return Requeued.MatchFamilyMember(context);

            var familyContext = context as MethodCall<IndexT, RetT>;

            return familyContext == null || Index.Equals(familyContext.Index);
        }

    }

}
